using Unity.Netcode;
using UnityEngine;

namespace Since.Netcode
{
	public enum E_NetcodeAuthority
	{
		Server,
		Client,
	}

	public class NetworkBehaviourPro<T, TServer, TClient, TClientSimulate, TAuthority, TNonAuthority> : NetworkBehaviour
	where T : NetworkBehaviourPro<T, TServer, TClient, TClientSimulate, TAuthority, TNonAuthority>
	where TServer : INetworkServer<T>, new()
	where TClient : INetworkClient<T>, new()
	where TClientSimulate : INetworkClientSimulate<T>, new()
	where TAuthority : INetworkAuthority<T>, new()
	where TNonAuthority : INetworkNonAuthority<T>, new()
	{
		TServer _Server;
		TClient _Client;
		TClientSimulate _ClientSimulate;
		TNonAuthority _NonAuthority;
		protected TNonAuthority NonAuthority => _NonAuthority;


		[SerializeField]
		E_NetcodeAuthority _Authority = E_NetcodeAuthority.Client;

		public E_NetcodeAuthority Authority => _Authority;

		void Update()
		{
			_Server?.InnerUpdate();
			_Client?.InnerUpdate();
			_ClientSimulate?.InnerUpdate();
		}

		void LateUpdate()
		{
			_Server?.InnerLateUpdate();
			_Client?.InnerLateUpdate();
			_ClientSimulate?.InnerLateUpdate();
		}

		void FixedUpdate()
		{
			_Server?.InnerFixedUpdate();
			_Client?.InnerFixedUpdate();
			_ClientSimulate?.InnerFixedUpdate();
		}

		void OnEnable()
		{
			_Server?.InnerOnEnable();
			_Client?.InnerOnEnable();
			_ClientSimulate?.InnerOnEnable();
		}

		void OnDisable()
		{
			_Server?.InnerOnDisable();
			_Client?.InnerOnDisable();
			_ClientSimulate?.InnerOnDisable();
		}

		public override void OnDestroy()
		{
			_Server?.InnerOnDestroy();
			_Client?.InnerOnDestroy();
			_ClientSimulate?.InnerOnDestroy();

			base.OnDestroy();
		}

		void OnApplicationPause(bool pauseStatus)
		{
			_Server?.InnerOnApplicationPause(pauseStatus);
			_Client?.InnerOnApplicationPause(pauseStatus);
			_ClientSimulate?.InnerOnApplicationPause(pauseStatus);
		}

		void OnApplicationQuit()
		{
			_Server?.InnerOnApplicationQuit();
			_Client?.InnerOnApplicationQuit();
			_ClientSimulate?.InnerOnApplicationQuit();
		}

		public override void OnNetworkSpawn()
		{
			if (IsServer)
			{
				_Server = new TServer();
				_Server.SetNetworkBehaviour((T)this);

				if (_Authority == E_NetcodeAuthority.Client)
				{
					if (!IsHost)
					{
						_NonAuthority = new TNonAuthority();
						_NonAuthority.SetNetworkBehaviour((T)this);
						_Server.SetNonAuthority(_NonAuthority);
					}
				}
				else
				{
					var authority = new TAuthority();
					authority.SetNetworkBehaviour((T)this);
					_Server.SetAuthority(authority);
				}

				_Server.InnerOnNetworkSpawn();
			}

			if (IsClient)
			{
				if (IsOwner)
				{
					_Client = new TClient();
					_Client.SetNetworkBehaviour((T)this);

					if (_Authority == E_NetcodeAuthority.Client)
					{
						var authority = new TAuthority();
						authority.SetNetworkBehaviour((T)this);
						_Client.SetAuthority(authority);
					}
					else
					{
						if (!IsHost)
						{
							_NonAuthority = new TNonAuthority();
							_NonAuthority.SetNetworkBehaviour((T)this);
							_Client.SetNonAuthority(_NonAuthority);
						}
					}

					_Client.InnerOnNetworkSpawn();
				}
				else
				{
					_NonAuthority = new TNonAuthority();
					_NonAuthority.SetNetworkBehaviour((T)this);

					_ClientSimulate = new TClientSimulate();
					_ClientSimulate.SetNetworkBehaviour((T)this);
					_ClientSimulate.SetNonAuthority(_NonAuthority);
					_ClientSimulate.InnerOnNetworkSpawn();
				}
			}
		}

		public override void OnNetworkDespawn()
		{
			_Server?.InnerOnNetworkDespawn();
			_Client?.InnerOnNetworkDespawn();
			_ClientSimulate?.InnerOnNetworkDespawn();
		}

		public override void OnGainedOwnership()
		{
			_Server?.InnerOnGainedOwnership();
			_Client?.InnerOnGainedOwnership();
			_ClientSimulate?.InnerOnGainedOwnership();
		}

		public override void OnLostOwnership()
		{
			_Server?.InnerOnLostOwnership();
			_Client?.InnerOnLostOwnership();
			_ClientSimulate?.InnerOnLostOwnership();
		}

		protected override void OnOwnershipChanged(ulong previousOwner, ulong newOwner)
		{
			_Server?.InnerOnOwnershipChanged(previousOwner, newOwner);
			_Client?.InnerOnOwnershipChanged(previousOwner, newOwner);
			_ClientSimulate?.InnerOnOwnershipChanged(previousOwner, newOwner);
		}

		public override void OnNetworkObjectParentChanged(NetworkObject parentNetworkObject)
		{
			_Server?.InnerOnNetworkObjectParentChanged(parentNetworkObject);
			_Client?.InnerOnNetworkObjectParentChanged(parentNetworkObject);
			_ClientSimulate?.InnerOnNetworkObjectParentChanged(parentNetworkObject);
		}

		public override void OnReanticipate(double lastRoundTripTime)
		{
			_Server?.InnerOnReanticipate(lastRoundTripTime);
			_Client?.InnerOnReanticipate(lastRoundTripTime);
			_ClientSimulate?.InnerOnReanticipate(lastRoundTripTime);
		}
	}
}